/*
 * Copyright 2012-2021 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.boot.context.config;

import org.springframework.boot.context.properties.source.ConfigurationProperty;
import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
import org.springframework.boot.origin.Origin;
import org.springframework.core.env.PropertySource;

/**
 * 当尝试从非激活状态的 {@link ConfigData} 属性源解析属性时抛出的异常。
 * 用于确保用户不会意外尝试指定无法解析的属性。
 *
 * @author Phillip Webb
 * @author Madhura Bhave
 * @since 2.4.0
 */
public class InactiveConfigDataAccessException extends ConfigDataException {

	private final PropertySource<?> propertySource;

	private final ConfigDataResource location;

	private final String propertyName;

	private final Origin origin;

	/**
	 * 创建一个新的 {@link InactiveConfigDataAccessException} 实例。
	 * @param propertySource 非激活的属性源
	 * @param location 属性源对应的 {@link ConfigDataResource}，如果该源不是由 {@link ConfigData} 加载，则为 {@code null}
	 * @param propertyName 属性名称
	 * @param origin 属性的来源，或 {@code null}
	 */
	InactiveConfigDataAccessException(PropertySource<?> propertySource, ConfigDataResource location,
			String propertyName, Origin origin) {
		super(getMessage(propertySource, location, propertyName, origin), null);
		this.propertySource = propertySource;
		this.location = location;
		this.propertyName = propertyName;
		this.origin = origin;
	}

	private static String getMessage(PropertySource<?> propertySource, ConfigDataResource location, String propertyName,
			Origin origin) {
		StringBuilder message = new StringBuilder("Inactive property source '");
		message.append(propertySource.getName());
		if (location != null) {
			message.append("' imported from location '");
			message.append(location);
		}
		message.append("' cannot contain property '");
		message.append(propertyName);
		message.append("'");
		if (origin != null) {
			message.append(" [origin: ");
			message.append(origin);
			message.append("]");
		}
		return message.toString();
	}

	/**
	 * 返回包含该属性的非激活属性源。
	 * @return 属性源
	 */
	public PropertySource<?> getPropertySource() {
		return this.propertySource;
	}

	/**
	 * 返回属性源对应的 {@link ConfigDataResource}，如果该源不是由 {@link ConfigData} 加载，则返回 {@code null}。
	 * @return 配置数据位置或 {@code null}
	 */
	public ConfigDataResource getLocation() {
		return this.location;
	}

	/**
	 * 返回属性名称。
	 * @return 属性名称
	 */
	public String getPropertyName() {
		return this.propertyName;
	}

	/**
	 * 返回属性的来源，或 {@code null}。
	 * @return 属性来源
	 */
	public Origin getOrigin() {
		return this.origin;
	}

	/**
	 * 如果给定的 {@link ConfigDataEnvironmentContributor} 包含指定属性，则抛出 {@link InactiveConfigDataAccessException}。
	 * @param contributor 要检查的贡献者
	 * @param name 要检查的属性名
	 */
	static void throwIfPropertyFound(ConfigDataEnvironmentContributor contributor, ConfigurationPropertyName name) {
		ConfigurationPropertySource source = contributor.getConfigurationPropertySource();
		ConfigurationProperty property = (source != null) ? source.getConfigurationProperty(name) : null;
		if (property != null) {
			PropertySource<?> propertySource = contributor.getPropertySource();
			ConfigDataResource location = contributor.getResource();
			throw new InactiveConfigDataAccessException(propertySource, location, name.toString(),
					property.getOrigin());
		}
	}

}
